/*
 ************************************************************************************************
 *                                                                                              *
 *  OSA cooperative RTOS for Microchip PIC-controllers: PIC10/12/16/18/24/dsPIC                 *
 *                                                                                              *
 *  URL:            http://wiki.pic24.ru/doku.php/en/osa/ref/intro                              *
 *                  http://picosa.narod.ru                                                      *
 *                                                                                              *
 *----------------------------------------------------------------------------------------------*
 *                                                                                              *
 *  File:           osa_picc16.c                                                                *
 *                                                                                              *
 *  Compilers:      HT-PICC STD                                                                 *
 *                  HT-PICC18 STD                                                               *
 *                  Microchip C18                                                               *
 *                  Microchip C30                                                               *
 *                                                                                              *
 *  Programmer:     Timofeev Victor                                                             *
 *                  osa@pic24.ru, testerplus@mail.ru                                            *
 *                                                                                              *
 *  Description:    PICC16 specific functions overloads                                         *
 *                                                                                              *
 *                                                                                              *
 ************************************************************************************************
 */





#define OS_Init_ProcSpec()      // Empty macro for PIC16









/************************************************************************************************
 *                                                                                              *
 *  MAKE FSR POINTER TO OS_CUR_TASK                                                             *
 *                                                                                              *
 *  (for code reducing)                                                                         *
 *                                                                                              *
 ************************************************************************************************/


#if defined(_ROMSIZE) && (_ROMSIZE <= 0x800)

        asm("   global __OS_SET_FSR_CUR_TASK        ");
        asm("   global __OS_SET_FSR_CUR_TASK_W      ");
        asm("__OS_SET_FSR_CUR_TASK:                 ");
        asm("       movlw       0                   ");
        asm("__OS_SET_FSR_CUR_TASK_W:               ");
        asm("       addwf       __OS_CurTask, w     ");
        asm("       movwf       __fsr               ");

    #if OS_BANK_TASKS >= 2

        asm("       bsf         __status, 7         ");

    #else

        asm("       bcf         __status, 7         ");

    #endif

        asm("       return                          ");

#endif




















/*
 ************************************************************************************************
 *                                                                                              *
 *                            C O U N T I N G    S E M A P H O R E S                            *
 *                                                                                              *
 ************************************************************************************************
 */


//------------------------------------------------------------------------------
#ifdef OS_ENABLE_CSEM
//------------------------------------------------------------------------------


/*
 ********************************************************************************
 *                                                                              *
 *  void _OS_Csem_Signal (OST_CSEM *pCSem)                                      *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  Increase counting semaphore. Set EventError if csem is FF...                *
 *                                                                              *
 *                                                                              *
 *  parameters:     pCSem - pointer to counting semaphore                       *
 *                                                                              *
 *  on return:      OS_IsEventError()                                           *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_Csem_Signal_DEFINED
//------------------------------------------------------------------------------

void _OS_Csem_Signal (OST_CSEM *pCSem)
{
    __OS_CSEM_DI();
    OS_Flags.bEventError = 0;       // Semaphore overflow flag

    //------------------------------------------------------------------------------
    // For one-byte semaphores
    #if OS_CSEM_SIZE == 1
        _fsr = (_FSR_TYPE)pCSem;
        #ifdef __OSAPICC__
            _status &= 0x7F;        // IRP = 0
        #endif
        _indf++;
        if (!_indf)
        {
            _indf--;
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }

    //------------------------------------------------------------------------------
    // for two-bytes semaphores
    #elif OS_CSEM_SIZE == 2
        _fsr = (_FSR_TYPE)pCSem;
        _status &= 0x7F;        // IRP = 0
        asm("\t    movlw    1     ");
        asm("\t    addwf    __indf,f    ");
        asm("\t    incf     __fsr, f    ");
        asm("\t    btfsc    __status, 0     ");
        asm("\t    incf     __indf,f     ");
        asm("\t    movf     __indf, w     ");
        asm("\t    decf     __fsr, f     ");
        asm("\t    iorwf    __indf, w      ");
        if (ZERO) {
            asm("\t    movlw    -1     ");
            asm("\t    movwf    __indf     ");
            asm("\t    incf     __fsr,f     ");
            asm("\t    movwf    __indf     ");
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }
    //------------------------------------------------------------------------------
    // for four-bytes semaphores
    #else
        (*pCSem)++;
        if (!*pCSem) {
            (*pCSem) = -1;
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }
    #endif
    //------------------------------------------------------------------------------
    __OS_CSEM_RI();
}



//------------------------------------------------------------------------------
#if defined (OS_ENABLE_INT_CSEM)
//------------------------------------------------------------------------------

/*
 ********************************************************************************
 *                                                                              *
 *   void _OS_Csem_Signal_I (OST_CSEM *pCSem)                                   *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    Copy of _OS_Csem_Signal function for calling from interrupt *
 *                                                                              *
 *                                                                              *
 *  parameters:     pCSem - pointer to counting semaphore                       *
 *                                                                              *
 *  on return:      OS_IsEventError()                                           *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_Csem_Signal_I_DEFINED
//------------------------------------------------------------------------------

void _OS_Csem_Signal_I (OST_CSEM *pCSem)
{
    OS_Flags.bEventError = 0;       // Semaphore overflow flag

    //------------------------------------------------------------------------------
    // For one-byte semaphores
    #if OS_CSEM_SIZE == 1
        _fsr = (_FSR_TYPE)pCSem;
        #ifdef __OSAPICC__
            _status &= 0x7F;        // IRP = 0
        #endif
        _indf++;
        if (!_indf)
        {
            _indf--;
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }

    //------------------------------------------------------------------------------
    // for two-bytes semaphores
    #elif OS_CSEM_SIZE == 2
        _fsr = (_FSR_TYPE)pCSem;
        _status &= 0x7F;        // IRP = 0
        asm("\t    movlw    1     ");
        asm("\t    addwf    __indf,f    ");
        asm("\t    incf     __fsr, f    ");
        asm("\t    btfsc    __status, 0     ");
        asm("\t    incf     __indf,f     ");
        asm("\t    movf     __indf, w     ");
        asm("\t    decf     __fsr, f     ");
        asm("\t    iorwf    __indf, w      ");
        if (ZERO) {
            asm("\t    movlw    -1     ");
            asm("\t    movwf    __indf     ");
            asm("\t    incf     __fsr,f     ");
            asm("\t    movwf    __indf     ");
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }
    //------------------------------------------------------------------------------
    // for four-bytes semaphores
    #else
        (*pCSem)++;
        if (!*pCSem) {
            (*pCSem) = -1;
            OS_Flags.bEventError = 1;   // Set error flag if overflow
        }
    #endif
    //------------------------------------------------------------------------------
}

//------------------------------------------------------------------------------
#endif  //OS_ENABLE_INT_CSEM
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#endif  // OS_ENABLE_CSEM
//------------------------------------------------------------------------------



















/*
 ************************************************************************************************
 *                                                                                              *
 *                         C R I T I C A L   S E C T I O N S                                    *
 *                                                                                              *
 ************************************************************************************************
 */

//------------------------------------------------------------------------------
#ifdef OS_ENABLE_CRITICAL_SECTION
//------------------------------------------------------------------------------


/*
 ********************************************************************************
 *                                                                              *
 *  void OS_EnterCriticalSection (void)                                         *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    Enter critical section.                                     *
 *                  This function disables interrupts (with saving current      *
 *                  state) and sets system flag bInCriticalSection              *
 *                                                                              *
 *  parameters:     none                                                        *
 *                                                                              *
 *  on return:      none                                                        *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define OS_EnterCriticalSection_DEFINED
//------------------------------------------------------------------------------

void OS_EnterCriticalSection (void)
{
    _OST_INT_TYPE temp;
    temp = OS_DI();
    OS_Flags.bInCriticalSection = 1;

    OS_Flags.bGIE_CTemp = 0;
    if (temp & 0x80) OS_Flags.bGIE_CTemp = 1;

}



/*
 ********************************************************************************
 *                                                                              *
 *   void OS_LeaveCriticalSection (void)                                        *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    Leave critical section.                                     *
 *                  This function restore interrupt state from OS_Flag temp bits*
 *                  and clears system flag bInCriticalSection                   *
 *                                                                              *
 *  parameters:     none                                                        *
 *                                                                              *
 *  on return:      none                                                        *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define OS_LeaveCriticalSection_DEFINED
//------------------------------------------------------------------------------

void OS_LeaveCriticalSection (void)
{
    char temp;
    OS_Flags.bInCriticalSection = 0;
    temp = 0;
    if (OS_Flags.bGIE_CTemp) temp |= 0x80;
    OS_RI(temp);
}

//------------------------------------------------------------------------------
#endif      //     #ifdef OS_ENABLE_CRITICAL_SECTION
//------------------------------------------------------------------------------


















/*
 ************************************************************************************************
 *                                                                                              *
 *                                   D E L A Y S   I N   T A S K                                *
 *                                                                                              *
 ************************************************************************************************
 */

//------------------------------------------------------------------------------
#ifdef OS_ENABLE_TTIMERS
//------------------------------------------------------------------------------

/*
 ********************************************************************************
 *                                                                              *
 *   void _OS_InitDelay (_OST_TTIMER Delay)                                     *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    (Internal function called by system kernel)                 *
 *                                                                              *
 *                  Init task timer delay.Set timer variable in task descriptor,*
 *                  sets bit bDelay and clears bit bCanContinue.                *
 *                                                                              *
 *                  _OS_CurTask must point to descriptor.                       *
 *                                                                              *
 *                                                                              *
 *  parameters:     Delay   - time of delay in system ticks                     *
 *                                                                              *
 *  on return:      none                                                        *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_InitDelay_DEFINED
//------------------------------------------------------------------------------


void _OS_InitDelay (_OST_TTIMER Delay)
{

    _OS_SET_FSR_CUR_TASK();
    _OS_bTaskCanContinue = 0;
    _OS_bTaskDelay = 0;         // Clear bDelay to prevent timer changing in interrupt

    if (Delay)
    {

        Delay ^= -1;
        Delay ++;
        _OS_CurTask->Timer = Delay;

        _OS_SET_FSR_CUR_TASK();
        _OS_bTaskDelay = 1;
        _OS_bTaskReady = 1;

    }
}

//------------------------------------------------------------------------------
#endif  // #ifdef OS_ENABLE_TTIMERS
//------------------------------------------------------------------------------






























/************************************************************************************************
 *                                                                                              *
 *                            S Y S T E M   F U N C T I O N S                                   *
 *                                                                                              *
 ************************************************************************************************/



/*
 ********************************************************************************
 *                                                                              *
 *   void _OS_Task_Create(_OST_INT_TYPE priority, OST_CODE_POINTER TaskAddr)    *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *   description:   (Internal function called by system kernel from service     *
 *                  OS_Task_Create)                                             *
 *                  Create task in free descriptor.                             *
 *                                                                              *
 *  parameters:     priority - value from 0 (highest) to 7 (lowest)             *
 *                  TaskAddr - pointer to C-function that contains task         *
 *                                                                              *
 *  on return:      check OS_IsError                                            *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_Task_Create_DEFINED
//------------------------------------------------------------------------------

void _OS_Task_Create(_OST_INT_TYPE priority, OST_CODE_POINTER TaskAddr)
{
    OST_TASK_POINTER Task;

    OS_Flags.bError = 0;

    #if defined(OS_DISABLE_PRIORITY)
    priority = 0;
    #endif

    Task = (OST_TASK_POINTER)OS_TaskVars;
    _OS_temp = OS_TASKS;

    do {

        if (!Task->State.bEnable)
        {
            ((OST_TASK_STATE*)&priority)->bEnable = 1;
            ((OST_TASK_STATE*)&priority)->bReady = 1;


            Task->pTaskPointer = TaskAddr;

            #ifdef OS_ENABLE_TTIMERS
                Task->Timer = 0;
            #endif

            *((OS_TASKS_BANK char*)&Task->State) = priority;

            // Now FSR points to task descriptor

            OS_Flags.bError = 0;

            return ;
        }

        Task ++;

    } while (_OS_temp--);

    // No free descriptor

    OS_Flags.bError = 1;

    return ;
}







/*
 ********************************************************************************
 *                                                                              *
 *  char OS_DI (void)                                                           *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    Disable interrupt with GIE saving                           *
 *                                                                              *
 *  parameters:     none                                                        *
 *                                                                              *
 *  on return:      char - bit 7 = previous GIE value, bits 0..6 = 0            *
 *                                                                              *
 ********************************************************************************
 */

//-------------------------------------------------------------------------------
#define OS_DI_DEFINED
//-------------------------------------------------------------------------------

    asm("   global _OS_DI           ");
    asm("_OS_DI:                    ");
    asm("   clrf    __status        ");
    asm("   movf    __intcon, w      ");
    asm("   andlw   0x80            ");
    asm("_DI_REPEAT:                ");
    asm("   bcf     __intcon, 7      ");
    asm("   btfss   __intcon, 7      ");
    asm("   return                  ");
    asm("   goto    _DI_REPEAT      ");











/*
 ********************************************************************************
 *                                                                              *
 *  bit _OS_CheckEvent (char bEvent)                                            *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    (Internal function called by system kernel througth         *
 *                  waiting services)                                           *
 *                                                                              *
 *                  Check condition of bEvent. Accordint to bEvent value and    *
 *                  current state of bReady, bDelay and bCanContinue flags,     *
 *                  task becames ready to execute or still remains in waiting   *
 *                  mode.                                                       *
 *                                                                              *
 *  parameters:     bEvent - zero, or non-zero condition                        *
 *                                                                              *
 *  on return:      1 - when condition is true, or timeout occured              *
 *                  0 - condition is false and no timeout                       *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_CheckEvent_DEFINED
//------------------------------------------------------------------------------

bit _OS_CheckEvent (char bEvent)
{



    OS_Flags.bTimeout = 0;
    _OS_SET_FSR_CUR_TASK();
    _carry = 0;

    if (bEvent)
    {
        if (_OS_bTaskReady)
        {
            #if defined(OS_ENABLE_TTIMERS)
        EXIT_OK:
            #endif

            _carry = 1;
        }

        _OS_bTaskReady = 1;

        #if defined(OS_ENABLE_TTIMERS)
            if (_carry) _OS_bTaskDelay = 0;
        #endif

        asm("   return     ");

    }

    _OS_bTaskReady = 0;

    #if defined(OS_ENABLE_TTIMERS)

        asm("   btfss   __indf, 4     ");
        asm("   btfss   __indf, 5     ");
        asm("   return                ");
        OS_Flags.bTimeout = 1;
        goto EXIT_OK;

    #else

        asm("   return  ");

    #endif

}






















/************************************************************************************************
 *                                                                                              *
 *                     W O R K   W I T H   D Y N A M I C   T I M E R S                          *
 *                                                                                              *
 ************************************************************************************************/

//-----------------------------------------------------------------
#ifdef OS_ENABLE_DTIMERS
//-----------------------------------------------------------------


/*
 ********************************************************************************
 *                                                                              *
 *  void _OS_Dtimer_Create (OST_DTIMER *dtimer)                                 *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    (Internal function called by system kernel througth         *
 *                  service OS_Dtimer_Create)                                   *
 *                                                                              *
 *                  Add dynamic timer into list of active dynamic timers. This  *
 *                  timer will be added at first position in list.              *
 *                                                                              *
 *                                                                              *
 *  parameters:     dtimer - pointer to dynamic timer.                          *
 *                  (for PICC16 can be allocated only in bank0 and bank1)       *
 *                                                                              *
 *  on return:      none                                                        *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_Dtimer_Create_DEFINED
//------------------------------------------------------------------------------

void _OS_Dtimer_Create (OST_DTIMER *dtimer)
{
    _OS_temp = OS_DI();

    _irp = 0;
    _fsr = (_FSR_TYPE) dtimer;
    _indf = *((OS_BANK char*)&_OS_DTimers.Flags);
    // Here: bActive is set,
    //       bTimeout is cleared,
    //       bRun is cleared
    //       bNextEnable is copied

    _fsr++;
    _indf = (_FSR_TYPE)_OS_DTimers.Next;

    _fsr++;
    _indf = 0;
    _fsr++;
    #if  OS_DTIMER_SIZE >= 2
        _indf = 0;
        _fsr++;
    #endif
    #if  OS_DTIMER_SIZE == 4
        _indf = 0;
        _fsr++;
        _indf = 0;
    #endif


    _OS_DTimers.Next = (OST_DTIMER*)dtimer;
    _OS_DTimers.Flags.bNextEnable = 1;

    #if !defined(__OSAPICC12__)
    OS_RI(_OS_temp);
    #endif
}





/*
 ********************************************************************************
 *                                                                              *
 *  void _OS_Dtimer_Delete (OST_DTIMER *dtimer)                                 *
 *                                                                              *
 *------------------------------------------------------------------------------*
 *                                                                              *
 *  description:    (Internal function called by system kernel througth         *
 *                  service OS_Dtimer_Delete)                                   *
 *                                                                              *
 *                  Delete dynamic timer from list of active timers. It is      *
 *                  recommended to delete all unused dynamic timers to avoid    *
 *                  speed down.                                                 *
 *                                                                              *
 *  parameters:     dtimer - pointer to deleting dynamic timer                  *
 *                                                                              *
 *  on return:      none                                                        *
 *                                                                              *
 *  note:           This function overloads "osa.c"                             *
 *                                                                              *
 ********************************************************************************
 */

//------------------------------------------------------------------------------
#define _OS_Dtimer_Delete_DEFINED
//------------------------------------------------------------------------------

void _OS_Dtimer_Delete (OST_DTIMER *dtimer)
{

    OST_DTIMER *ptcb;
    char flags;

    _irp = 0;
    _fsr = ((_FSR_TYPE)dtimer);
    flags = _indf;
    _OS_ClearIndfTimerActive;
    _fsr++;
    ptcb = (OST_DTIMER *)_indf;
    _fsr = (_FSR_TYPE)&_OS_DTimers;
    // Search timer in list of active timers
REPEAT:
    if (_OS_CheckIndfTimerNextEnable)   // exit if timer not found
    {
        _fsr++;
        if ((_FSR_TYPE)_indf == (_FSR_TYPE)dtimer)  // Check pointer to next timer
        {
            _OS_temp = OS_DI();         // Disable interrupts
            _indf = *((char*)&ptcb);    // Change pointer
            _fsr--;
            // Clear NextEnable flag if it was last timer
            if (!((OST_DTIMER_FLAGS*)&flags)->bNextEnable) _OS_ClearIndfTimerNextEnable;
            OS_RI(_OS_temp);
            return;
        }
        _fsr = (_FSR_TYPE)_indf;
        goto REPEAT;
    }

}



//------------------------------------------------------------------------------
#endif      //  OS_ENABLE_DTIMERS
//------------------------------------------------------------------------------























